#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>

#include "tools.h"
#include "benc.h"
#include "metainfo.h"
#include "tracker.h"
#include "torrent.h"
#include "net.h"
#include "peer.h"

#if 0
static int
is_rfc2396_alnum( char ch )
{
    return ( '0' <= ch && ch <= '9' )
        || ( 'A' <= ch && ch <= 'Z' ) || ( 'a' <= ch && ch <= 'z' )
        || ch == '.' || ch == '-' || ch == '_' || ch == '~';
}
#endif

static void xx_url_encode( char * out, const uint8_t * in, size_t in_len ) /* rfc2396 */
{
    const uint8_t *end = in + in_len;

	if ( NULL == out || NULL == in ) {
		xx_dbg("Input para Invalid\n");
		xx_assert(0);
		return;
	}

    while( in != end )
//        if( is_rfc2396_alnum( *in ) )
//            *out++ = (char) *in++;
//        else
            out += snprintf( out, 4, "%%%02x", (unsigned int)*in++ );

    *out = '\0';
}

int xx_load_torrent(const char *fileName, xx_metainfo *meta)
{
	int			 ret	  = 0;
	int			 fd		  = 0; 
	int			 size	  = 0; 
	int			 buff_len = 0;
	char		*buffer	  = NULL;
	char		*buf	  = NULL;

	if ( NULL == fileName || NULL == meta) {
		xx_assert(0);
		return -1;
	}

	fd = open(fileName, O_RDONLY);
	if ( fd == -1 ) {
		return -1;
	}

	size = lseek(fd, 0, SEEK_END);
	if ( size == -1 ) {
		xx_dbg("lseek fail\n");
		return -1;
	}
	if ( lseek(fd, 0, SEEK_SET) ) {
		xx_dbg("lseek fail\n");
		return -1;
	}
	xx_dbg("torrent size: %d\n", size);

	buff_len = size;

	buffer = (char *)malloc(size + 1);
	if ( buffer == NULL ) {
		xx_dbg("malloc fail\n");
		return -1;
	}

	buf = buffer;
	while ( (ret = read(fd, buf, size)) > 0 ) {
		buf += ret;
		size -= ret;
	}
	if ( 0 != ret ) {
		return ret;
	}

	if ( -1 == parse_metainfo(buffer, buff_len, meta)) {
		xx_err("parse meta error\n");
		return -1;
	}

	free(buffer);
	buffer = NULL;

	return 0;
}

int xx_torrent_init(char *torrent_name, xx_torrent *torrent)
{

	if ( NULL == torrent_name || NULL == torrent ) {
		xx_err("input para error!\n");	
		xx_assert(0);
		return 0;
	}

	torrent->metainfo = (xx_metainfo *)malloc(sizeof(xx_metainfo));
	if ( -1 == xx_load_torrent(torrent_name, torrent->metainfo) ) {
		xx_err("load_torrent error\n");
		return -1;
	}

//	show_metainfo(torrent->metainfo);

	memcpy(torrent->torrent_path, torrent_name, strlen(torrent_name) + 1); 

	torrent->listen_port = LISTEN_PORT;
	torrent->listen_fd = xx_listen_port(&torrent->listen_port);

	torrent->state = STAT_IDLE;
	memcpy(torrent->event, "started", strlen("started"));

//	torrent->announc
	memcpy(torrent->peer_id, "-xx-", 4);
	get_rand_string('a', 'z', 16, &torrent->peer_id[4]);

	torrent->left = torrent->metainfo->files[0].length;
	xx_url_encode(torrent->info_hash, torrent->metainfo->hash_str, 20);

	if ( xx_tracker_init(torrent) < 0 ) {
		xx_err("xx_tracker_init failed");
		return -1;
	}
	xx_peer_init(&torrent->peer_list, &torrent->peer_fds);

	return 0;
}

void xx_torrent_start(xx_torrent *torrent)
{
	pthread_t		tracker_tid, peer_tid;
	void			*arg = torrent;

	if ( NULL == torrent ) {
		xx_dbg("Input para Invalid\n");
		xx_assert(0);
		return;
	}

	pthread_create(&tracker_tid, NULL, xx_tracker_handler, arg);
	pthread_create(&peer_tid, NULL, xx_peer_start, arg);

//	free_metainfo(&torrent);
}




